//
// Created by hunan on 24-12-2.
//

#include "openssl_util.h"

#include <cstdint>
#include <cstdio>
#include <fcntl.h>
#include <unistd.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/md5.h>
#include <openssl/sha.h>

void OpenSSLUtil::EncodeDataToMD5(const std::string & data, std::string & result)
{
    EncodeDataToMD5(reinterpret_cast<const unsigned char *>(data.c_str()), data.size(), result);
}
void OpenSSLUtil::EncodeDataToMD5(const unsigned char * data, int data_length, std::string & result)
{
    unsigned char md5_result[MD5_DIGEST_LENGTH]             = {};
    char          md5_hex_result[MD5_DIGEST_LENGTH * 2 + 1] = {};
    MD5(data, data_length, md5_result);
    Hex(md5_result, MD5_DIGEST_LENGTH, md5_hex_result, sizeof(md5_hex_result));
    result = md5_hex_result;
}

int OpenSSLUtil::ComputeFileMD5(const std::string & file, std::string & result)
{
    int file_description = open(file.c_str(), O_RDONLY, 0666);
    if (file_description < 0)
    {
        return -1;
    }
    MD5_CTX ctx = {};
    MD5_Init(&ctx);
    unsigned char buffer[65535] = {};
    int read_length = 0;
    while ((read_length = read(file_description, buffer, sizeof(buffer))) > 0)
    {
        MD5_Update(&ctx, buffer, read_length);
    }
    unsigned char md[MD5_DIGEST_LENGTH] = {};
    MD5_Final(md, &ctx);
    char          md5_hex_result[MD5_DIGEST_LENGTH * 2 + 1] = {};
    Hex(md, MD5_DIGEST_LENGTH, md5_hex_result, sizeof(md5_hex_result));
    result = md5_hex_result;
    close(file_description);
    return 0;
}

void OpenSSLUtil::EncodeDataSHA256(const std::string & data, std::string & result)
{
    unsigned char sha256_result[SHA256_DIGEST_LENGTH]             = {0};
    char          sha256_hex_result[SHA256_DIGEST_LENGTH * 2 + 1] = {0};
    SHA256(reinterpret_cast<const unsigned char *>(data.c_str()), data.size(), sha256_result);
    Hex(sha256_result, SHA256_DIGEST_LENGTH, sha256_hex_result, sizeof(sha256_hex_result));
    result = sha256_hex_result;
}
void OpenSSLUtil::EncodeDataSHA1(const std::string & data, std::string & result)
{
    EncodeDataSHA1(reinterpret_cast<const unsigned char *>(data.c_str()), data.size(), result);
}

void OpenSSLUtil::EncodeDataSHA1(const uint8_t * data, int data_length, std::string & result)
{
    unsigned char sha1_result[SHA_DIGEST_LENGTH]             = {0};
    char          sha1_hex_result[SHA_DIGEST_LENGTH * 2 + 1] = {0};
    SHA1(data, data_length, sha1_result);
    Hex(sha1_result, SHA_DIGEST_LENGTH, sha1_hex_result, sizeof(sha1_hex_result));
    result = sha1_hex_result;
}

void OpenSSLUtil::EncodeHMACSHA1(const uint8_t * data, int data_length, const std::string & key, std::string & result)
{
    unsigned char result_data[SHA_DIGEST_LENGTH]             = {};
    char          sha1_hex_result[SHA_DIGEST_LENGTH * 2 + 1] = {};
    unsigned int  result_length                              = 0;
    HMAC(EVP_sha1(), (const unsigned char *)key.c_str(), key.size(), data, data_length, result_data, &result_length);
    Hex(result_data, result_length, sha1_hex_result, sizeof(sha1_hex_result));
    result = sha1_hex_result;
}

void OpenSSLUtil::EncodeHMACSHA1(const std::string & data, const std::string & key, std::string & result)
{
    EncodeHMACSHA1(reinterpret_cast<const unsigned char *>(data.c_str()), data.size(), key, result);
}

void OpenSSLUtil::Hex(const unsigned char * data, int length, char * result, int result_length)
{
    for (int index = 0; index < length; index++)
    {
        int pos = index * 2;
        snprintf(result + pos, result_length - pos, "%02x", data[index]);
    }
}
